/**
 * @(#)AdministradorConfiguracionProperties.java 1/04/2010
 *
 * Copyright (c) 2005-2010 PsiqueWare S.A. de C.V. All Rights Reserved.
 * This software is the proprietary information PsiqueWare S.A. de C.V.
 * Use is subject to license terms.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * <br/>Usuario : AngelElectrico
 * <br/>Creaci&oacute;n: 1/04/2010 - 04:02:30 PM
 * <br/>
 * <br/>
 * <br/><t5>Cambios :</t5>
 * <br/><b>Fecha | Autor  |  Cambios</b>
 * <ul>
 * <li>1/04/2010 | <a href = "mailto:AngelElectrico@PsiqueWare.com">&Aacute;ngel El&eacute;ctrico</a> | Creaci&oacute;n del Objeto.</li>
 *
 * </ul>
 */

package com.psiqueware.alheli.configuracion.properties;

import com.psiqueware.alheli.configuracion.AbstractAdministradorConfiguracion;
import com.psiqueware.alheli.configuracion.AdministradorConfiguracion;
import com.psiqueware.alheli.exception.NullArgumentException;
import com.psiqueware.alheli.logging.Logger;
import com.psiqueware.alheli.logging.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Properties;

/**
 * {@link AdministradorConfiguracion} basado en un archivo properties.
 * <br/>Paquete Original: com.psiqueware.alheli.configuracion.properties
 * <br/>Nombre  Original: AdministradorConfiguracionProperties
 * <br/>Oprima para ver : <a href="AdministradorConfiguracionProperties.java.html"><i>C&oacute;digo Fuente</i> </a>
 *
 * @author <a href="mailto:AngelElectrico@PsiqueWare.com">AngelElectrico</a>
 * @version Revision: 1.1 1/04/2010 04:02:30 PM
 */
public class AdministradorConfiguracionProperties extends AbstractAdministradorConfiguracion implements AdministradorConfiguracion, Runnable
{

    /**
     * Logger de clase para AdministradorConfiguracionProperties
     */
    private static final Logger s_log = LoggerFactory.getLogger(AdministradorConfiguracionProperties.class);


    private final File m_archivoProperties;

    protected final Properties m_properties = new Properties();

    /**
     * Constructor de AdministradorConfiguracionProperties.
     */
    public AdministradorConfiguracionProperties(final File archivoProperties)
    {
        m_archivoProperties = archivoProperties;
        if (null != m_archivoProperties && m_archivoProperties.exists())
        {
            cargarProperties();
        }
    }


    /**
     * Carga las propiedades del archivo de propiedades.
     */
    private void cargarProperties()
    {
        if (s_log.isDebugEnabled()) s_log.debug("Cargando la configuracion desde: " + m_archivoProperties);
        FileInputStream fis = null;
        try
        {
            fis = new FileInputStream(m_archivoProperties);
            m_properties.load(fis);
        }
        catch (IOException e)
        {
            final String error = "<error><msg>El archivo de propiedades esta corrupto.</msg><parametros>" + m_archivoProperties + "</parametros></error>";
            s_log.error(error);
            s_log.error(e);
            throw new IllegalArgumentException(error, e);
        }
        finally
        {
            if (null != fis)
            {
                try
                {
                    fis.close();
                }
                catch (IOException e)
                {
                }
            }
        }
    }


    /**
     * Segundos antes de guardar el archivo.
     */
    private int segundosAntesGuardar = 0;


    /**
     * When an object implementing interface {@link Runnable} is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p/>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see Thread#run()
     */
    public void run()
    {
        while (segundosAntesGuardar > 0)
        {
            segundosAntesGuardar--;
            try
            {
                Thread.sleep(1000);
            }
            catch (InterruptedException e)
            {
            }
        }
        FileOutputStream fos = null;
        try
        {
            if (s_log.isDebugEnabled()) s_log.debug("Guardando: " + m_archivoProperties);
            fos = new FileOutputStream(m_archivoProperties);
            m_properties.store(fos, null);
            fos.close();
        }
        catch (Exception e)
        {
            final Date date = new Date();
            final String error = "No se pudo persistir el archivo de configuracion: " + m_archivoProperties + ".:" + e.getMessage();
            if (s_log.isDebugEnabled()) s_log.debug(error);
            throw new RuntimeException(e);
        }
        finally
        {
            if (null != fos)
            {
                try
                {
                    fos.close();
                }
                catch (IOException ignored)
                {
                }
            }
        }

    }

    /**
     * Agrega un atributo de configuraci&oaucte;n.
     *
     * @param idDominio identificador &uacute;nico del dominio al que pertence el contexto.
     * @param contexto  del atributo. No puede ser <code>null</code> ni vacio.
     * @param nombre    del atributo. No puede ser <code>null</code> ni vacio.
     * @param valor     a almacenar.
     */
    public void configura(final Long idDominio, String contexto, final String nombre, final String valor)
    {
        if (null == nombre) throw new NullArgumentException("<error>El argumento 'nombre' no puede ser NULL.</error>");
        if (0 == nombre.trim().length())
            throw new IllegalArgumentException("<error>El argumento 'nombre' es una cadena vacia.</error>");

        final String llave = generaLlave(idDominio, contexto, nombre);
        m_properties.setProperty(llave, valor == null ? "null" : valor);

        //Esperamos 3 segundos por mas atributos antes de guardar.
        if (segundosAntesGuardar == 0 && null != m_archivoProperties)
        {
            segundosAntesGuardar = 3;

            new Thread(this).start();
        }
        else
        {
            segundosAntesGuardar = 3;
        }

    }

    /**
     * Genera una llave a partir de los datos del valor.
     *
     * @param idDominio identificador &uacute;nico del dominio al que pertence el contexto.
     * @param contexto  del atributo. No puede ser <code>null</code> ni vacio.
     * @param nombre    del atributo. No puede ser <code>null</code> ni vacio.
     *
     * @return llave para estos datos.
     */
    private String generaLlave(final Long idDominio, String contexto, final String nombre)
    {
        if (null == contexto) contexto = "null";
        else contexto = contexto.replace(".", "_");
        //eliminamos cualquier espacio en el contexto y en el nombre.
        while (contexto.contains(" ")) contexto.replace(" ", "");
        while (nombre.contains(" ")) nombre.replace(" ", "");

        if (0 == nombre.trim().length())
            throw new IllegalArgumentException("<error>El argumento 'nombre' es una cadena vacia.</error>");
        if (0 == contexto.trim().length())
            throw new IllegalArgumentException("<error>El argumento 'contexto' es una cadena vacia.</error>");

        return String.valueOf(idDominio) + '.' + String.valueOf(contexto) + '.' + String.valueOf(nombre);
    }

    /**
     * Agrega un atributo de configuraci&oaucte;n.
     *
     * @param contexto del atributo. No puede ser <code>null</code> ni vacio.
     * @param nombre   del atributo. No puede ser <code>null</code> ni vacio.
     * @param valor    a almacenar.
     */
    public void configura(final String contexto, final String nombre, final String valor)
    {
        configura(null, contexto, nombre, valor);
    }

    /**
     * Agrega un atributo de configuraci&oaucte;n.
     *
     * @param nombre del atributo. No puede ser <code>null</code> ni vacio.
     * @param valor  a almacenar.
     */
    public void configura(final String nombre, final String valor)
    {
        configura(null, null, nombre, valor);
    }

    /**
     * Busca un atributo de  configuraci&oaucte;n.
     *
     * @param idDominio       identificador &uacute;nico del dominio al que pertence el contexto.
     * @param contexto        del atributo buscado.
     * @param nombre          del atributo buscado. No puede ser <code>null</code> ni vacio.
     * @param valorPorDefecto en caso de que el atributo no se haya dado de alta.
     *
     * @return cadena con el valor guardado del atributo; el valorPorDefecto en caso de no tener almacenado ning&uacute;n valor.
     */
    public String busca(final Long idDominio, final String contexto, final String nombre, final String valorPorDefecto)
    {
        if (null == nombre) throw new NullArgumentException("<error>El argumento 'nombre' no puede ser NULL.</error>");
        if (0 == nombre.trim().length())
            throw new IllegalArgumentException("<error>El argumento 'nombre' es una cadena vacia.</error>");

        final String llave = generaLlave(idDominio, contexto, nombre);
        String valor = m_properties.getProperty(llave);
        if (null == valor || "null".equals(valor)) valor = valorPorDefecto;
        return valor;
    }

    /**
     * Busca un atributo de  configuraci&oaucte;n.
     *
     * @param contexto        del atributo buscado.
     * @param nombre          del atributo buscado. No puede ser <code>null</code> ni vacio.
     * @param valorPorDefecto en caso de que el atributo no se haya dado de alta.
     *
     * @return cadena con el valor guardado del atributo; el valorPorDefecto en caso de no tener almacenado ning&uacute;n valor.
     */
    public String busca(final String contexto, final String nombre, final String valorPorDefecto)
    {
        return busca(null, contexto, nombre, valorPorDefecto);
    }

    /**
     * Busca un atributo de  configuraci&oaucte;n.
     *
     * @param nombre          del atributo buscado. No puede ser <code>null</code> ni vacio.
     * @param valorPorDefecto en caso de que el atributo no se haya dado de alta.
     *
     * @return cadena con el valor guardado del atributo; el valorPorDefecto en caso de no tener almacenado ning&uacute;n valor.
     */
    public String busca(final String nombre, final String valorPorDefecto)
    {
        return busca(null, null, nombre, valorPorDefecto);
    }
}

